﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BasicFramework.Components;
using BasicFramework.Helper;
using System.Diagnostics;

namespace BasicFramework.Entities
{
    public class EntityManager : ICollection<IEntity>, IDisposable
    {
        #region Fields

        private Boolean disposed;
        private List<IEntity> entities;
        private bool isReadOnly;

        #endregion


        #region Properties

        public List<IEntity> Entities
        {
            get { return entities; }
        }

        #endregion


        #region Constructors

        public EntityManager()
        {
            this.entities = new List<IEntity>();
        }

        #endregion

        #region Disposing

        ~EntityManager()
        {
            Dispose(false);
        }

        protected virtual void Dispose(Boolean disposing)
        {
            // Check to see if Dispose has already been called.
            if (!this.disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources.
                if (disposing)
                {
                    // Dispose managed resources.
                    foreach (IEntity entity in this.entities)
                    {
                        entity.Dispose();
                    }

                }

                // Release unmanaged resources. If disposing is false, 
                // only the following code is executed.

            }
            disposed = true;
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        #endregion

        #region Events

        public event EventHandler<EntityEventArgs> EntityAdded;
        public event EventHandler<EntityEventArgs> EntityRemoved;

        #endregion


        #region Methods

        internal void Draw(float frameTime)
        {
            foreach (IEntity entity in this.entities)
            {
                if (entity.Enabled)
                {
                    entity.Draw(frameTime);
                }
            }
        }

        internal void Update(float frameTime)
        {
            foreach (IEntity entity in this.entities)
            {
                if (entity.Enabled)
                {
                    entity.Update(frameTime);
                }
            }
        }

        public void LoadResources(bool reloadAll)
        {
            foreach (IEntity entity in this)
            {
                entity.LoadResources(reloadAll);
            }
        }

        public void UnloadResources(bool reloadAll)
        {
            foreach (IEntity entity in this)
            {
                entity.UnloadResources(reloadAll);
            }
        }

        internal void Start()
        {
            foreach (IEntity entity in Entities)
            {
                entity.Start();
            }
        }

        public IEntity Find(string name)
        {
            return this.entities.Find(x => x.Name == name);
        }

        public IEntity FindWith<T>() where T : IComponent
        {
            return this.entities.Find(x => x.Contains<T>());
        }

        public List<IEntity> FindAllWith<T>() where T : IComponent
        {
            List<IEntity> matches = new List<IEntity>();

            foreach (IEntity entity in entities)
            {
                if (entity.Contains<T>())
                {
                    matches.Add(entity);
                }
            }
            return matches;
        }

        public T Find<T>(string name) where T : IComponent
        {
            foreach (IEntity entity in entities.Where(x => x.Name == name))
            {
                T temp = entity.Find<T>();
                if (temp != null)
                {
                    return temp;
                }
            }

            return default(T);
        }

        private void OnEntityAdded(IEntity item)
        {
            Logger.Info("Entity " + item.Name + " added");
            item.Start();

            if (this.EntityAdded != null)
            {
                this.EntityAdded(this, new EntityEventArgs(item));
            }
        }

        private void OnEntityRemoved(IEntity item)
        {
            Logger.Info("Entity " + item.Name + " removed");
            if (this.EntityRemoved != null)
            {
                this.EntityRemoved(this, new EntityEventArgs(item));
            }
        }


        #endregion

        #region ICollection Member

        /// <summary>
        /// Returns an enumerator that iterates through the collection.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>1</filterpriority>
        public IEnumerator<IEntity> GetEnumerator()
        {
            return this.entities.GetEnumerator();
        }

        /// <summary>
        /// Returns an enumerator that iterates through a collection.
        /// </summary>
        /// <returns>
        /// An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
        /// </returns>
        /// <filterpriority>2</filterpriority>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        /// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
        public void Add(IEntity item)
        {
            if (item.Manager == null)
            {
                Logger.Error("Entity is not initialized correctly!");
                Debug.Fail("Entity is not initialized correctly");
                return;
            }
            if (this.entities.Contains(item))
            {
                Logger.Error("Entity already exists");
                Debug.Fail("Entity already exists");
            }
            this.entities.Add(item);
            this.OnEntityAdded(item);
        }



        /// <summary>
        /// Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. </exception>
        public void Clear()
        {
            foreach (IEntity entity in entities)
            {
                this.Remove(entity);
            }
        }

        /// <summary>
        /// Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"/> contains a specific value.
        /// </summary>
        /// <returns>
        /// true if <paramref name="item"/> is found in the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false.
        /// </returns>
        /// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
        public bool Contains(IEntity item)
        {
            return this.entities.Contains(item);
        }

        /// <summary>
        /// Copies the elements of the <see cref="T:System.Collections.Generic.ICollection`1"/> to an <see cref="T:System.Array"/>, starting at a particular <see cref="T:System.Array"/> index.
        /// </summary>
        /// <param name="array">The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied from <see cref="T:System.Collections.Generic.ICollection`1"/>. The <see cref="T:System.Array"/> must have zero-based indexing.</param><param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param><exception cref="T:System.ArgumentNullException"><paramref name="array"/> is null.</exception><exception cref="T:System.ArgumentOutOfRangeException"><paramref name="arrayIndex"/> is less than 0.</exception><exception cref="T:System.ArgumentException"><paramref name="array"/> is multidimensional.-or-The number of elements in the source <see cref="T:System.Collections.Generic.ICollection`1"/> is greater than the available space from <paramref name="arrayIndex"/> to the end of the destination <paramref name="array"/>.-or-Type <paramref name="T"/> cannot be cast automatically to the type of the destination <paramref name="array"/>.</exception>
        public void CopyTo(IEntity[] array, int arrayIndex)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <returns>
        /// true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </returns>
        /// <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param><exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
        public bool Remove(IEntity item)
        {
            if (item == null)
            {
                return false;
            }

            bool result = false;

            if (this.entities.Contains(item))
            {
                // Clear all components of the Entity
                item.Clear();
                // Delete entity
                result = this.entities.Remove(item);
                this.OnEntityRemoved(item);
            }


            return result;
        }


        /// <summary>
        /// Gets the number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </summary>
        /// <returns>
        /// The number of elements contained in the <see cref="T:System.Collections.Generic.ICollection`1"/>.
        /// </returns>
        public int Count
        {
            get { return this.entities.Count; }
        }

        /// <summary>
        /// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
        /// </summary>
        /// <returns>
        /// true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.
        /// </returns>
        public bool IsReadOnly
        {
            get { return this.isReadOnly; }
            set
            {
                if (this.isReadOnly != value)
                {
                    this.isReadOnly = value;
                }
            }
        }

        #endregion




    }
}
